package com.wchat.network;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import org.simpleframework.xml.core.Persister;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.first.wchat.util.ScanUtil;
import com.wchat.network.device.BroadCastTarget;
import com.wchat.network.device.Device;
import com.wchat.network.device.DeviceContext;
import com.wchat.network.device.Group;
import com.wchat.network.device.GroupParam;
import com.wchat.network.device.SendTarget;
import com.wchat.network.message.BroadCastSender;
import com.wchat.network.message.LeaveGroupMessage;
import com.wchat.network.message.Message;
import com.wchat.network.message.MessageHandler;
import com.wchat.network.message.MessageReceiver;
import com.wchat.network.message.ShareDirectoryMessage;
import com.wchat.network.message.TextMessage;
import com.wchat.network.message.WhoAmIMessage;
import com.wchat.network.message.file.AcceptFileMessage;
import com.wchat.network.message.file.FileMessage;
import com.wchat.network.message.file.FileMessageController;
import com.wchat.network.message.file.FileReadyMessage;
import com.wchat.network.message.file.ImageMessage;
import com.wchat.network.message.file.RequestFileMessage;
import com.wchat.network.message.share.ShareDirectory;
import com.wchat.network.message.share.ShareItem;
import com.wchat.util.UserSettings;

public class Controller implements MessageHandler, UserAction {

	private static Controller INSTANCE = null;

	private Context context;

	private DeviceContext deviceContext = new DeviceContext();

	private WhoAmIMessage myBroadcastMessage;

	private BroadCastSender broadCastSender;

	private MessageReceiver messageReceiver;

	private Controller() {

	}

	public static Controller getInstance() {
		if (INSTANCE == null)
			INSTANCE = new Controller();
		return INSTANCE;
	}

	public void start(Context context) {
		this.context = context;
		Device local = new Device(ScanUtil.getLocalAddress());
		long id = UserSettings.getSettingLong(context, UserSettings.Keys.ID);
		if (id == 0) {
			id = Configs.generateUniqueDeviceId();
			UserSettings.putSettingLong(context, UserSettings.Keys.ID, id);
		}
		local.setId(id);
		local.setName(UserSettings.getSettingString(context, UserSettings.Keys.USER_NAME));
		deviceContext.setLocal(local);

		myBroadcastMessage = Message.createWhoAmIMessage(local);

		/* start broadcast sender */
		broadCastSender = new BroadCastSender(myBroadcastMessage);
		broadCastSender.setPriority(Thread.NORM_PRIORITY - 1);
		broadCastSender.start();
		/* start message receiver and parser */
		messageReceiver = new MessageReceiver(context, local.getAddress());
		messageReceiver.start();
		/* start check expire thread in device context */
		deviceContext.startCheckExpire(context);

		/*
		 * send detect message, anyone who get this message will send out a
		 * WhoAmIMessage
		 */
		try {
			BroadCastTarget.getInstance().sendMessage(Message.createDetectMessage());
		} catch (IOException e) {
			Configs.log("send detect message failed", e);
		}
	}

	public void stop() {
		/* stop check expire thread in device context */
		deviceContext.stopCheckExpire();
		deviceContext.clean();
		/* stop send broadcast sender */
		broadCastSender.close();
		try {
			broadCastSender.join();
		} catch (Exception e) {

		}
		broadCastSender = null;
		/* send goodbye message, stop message receiver and parser */
		messageReceiver.close();
		try {
			messageReceiver.join();
		} catch (Exception e) {

		}
		messageReceiver = null;

		/* at last send goodbye message */
		try {
			BroadCastTarget.getInstance().sendMessage(Message.createGoodbyeMessage(getLocalDevice()));
		} catch (Exception e) {
			Configs.log("goodbye fail", e);
		}

		this.context = null;
		INSTANCE = null;
	}

	/* implement interface MessageHandler */
	@Override
	public void handle(Message message) {
		byte type = message.getType();
		if (type == Message.TYPE_WHOAMI) {
			WhoAmIMessage whoAmIMessage = (WhoAmIMessage) message;
			Device device = whoAmIMessage.getDevice();
			if (device.getId() == deviceContext.getLocal().getId())
				return;
			deviceContext.analyseBroadcast(context, whoAmIMessage);
		} else if (type == Message.TYPE_TEXT) {
			/* test code */
			TextMessage textMessage = (TextMessage) message;
			Device sender = deviceContext.getMessageDevice(textMessage);
			if (sender != null) {
				Toast.makeText(context, sender.getName() + ":" + textMessage.getText() + "[group=" + message.getGroup() + "]",
						Toast.LENGTH_LONG).show();
			}
		} else if (type == Message.TYPE_DETECT) {
			broadCastSender.broadcastNow();
		} else if (type == Message.TYPE_FILE) {
			/* test code */
			final Device sender = deviceContext.getMessageDevice(message);
			
			final FileMessage fileMessage = (FileMessage) message;
			long group = message.getGroup();
			if (group == Message.GROUP_SHARE) {
				try {
					sender.sendMessage(Message.createAcceptFileMessage(fileMessage));
				} catch (Exception e) {
					Configs.log("", e);
				}
				return;
			}
			String name = fileMessage.getFileInfo().fileName;
			long size = fileMessage.getFileInfo().size;
			AlertDialog.Builder builder = new AlertDialog.Builder(context);
			builder.setTitle("Receiver a file");
			builder.setMessage("Sender: " + sender.getName() + ", file: " + name + ", size: " + (size >> 10) + "KB");
			builder.setPositiveButton("Accept", new OnClickListener() {

				@Override
				public void onClick(DialogInterface dialog, int which) {
					try {
						sender.sendMessage(Message.createAcceptFileMessage(fileMessage));
					} catch (Exception e) {
						Configs.log("", e);
					}
				}
			});
			builder.setNegativeButton("Reject", new OnClickListener() {

				@Override
				public void onClick(DialogInterface dialog, int which) {
					try {
						sender.sendMessage(Message.createRejectFileMessage(fileMessage));
					} catch (Exception e) {
						Configs.log("", e);
					}
				}
			});
			builder.show();
		} else if (type == Message.TYPE_IMAGE) {
			/* test code */
			final Device sender = deviceContext.getMessageDevice(message);
			final ImageMessage imageMessage = (ImageMessage) message;
			try {
				sender.sendMessage(Message.createAcceptFileMessage(imageMessage));
			} catch (Exception e) {
				Configs.log("", e);
			}
		} else if (type == Message.TYPE_REJECT_FILE) {
			/* test code */
			Device sender = deviceContext.getMessageDevice(message);
			Toast.makeText(context, sender.getName() + " reject your file.", Toast.LENGTH_LONG).show();
		} else if (type == Message.TYPE_ACCEPT_FILE) {
			/* test code */
			AcceptFileMessage acceptFileMessage = (AcceptFileMessage) message;
			FileMessage fileMessage = acceptFileMessage.getOriginalMessage();
			fileMessage.setGroup(acceptFileMessage.getGroup());
			int localPort = FileMessageController.getInstance().prepareFileServer(context, fileMessage);
			Device sender = deviceContext.getMessageDevice(message);
			try {
				sender.sendMessage(Message.createFileReadyMessage(acceptFileMessage, localPort));
			} catch (Exception e) {
				Configs.log("", e);
			}
		} else if (type == Message.TYPE_FILE_READY) {
			FileReadyMessage fileReadyMessage = (FileReadyMessage) message;
			FileMessageController.getInstance().receiveFile(context, fileReadyMessage);
		} else if (type == Message.TYPE_LEAVE_GROUP) {
			LeaveGroupMessage leaveGroupMessage = (LeaveGroupMessage) message;
			deviceContext.leaveGroup(context, leaveGroupMessage.getLeaveGroupId(), leaveGroupMessage.getDeviceId());
		} else if (type == Message.TYPE_GOODBYE) {
			deviceContext.deviceLeave(context, message.getDeviceId());
		} else if (type == Message.TYPE_GET_SHARE_DIRECTORY) {
			/* send my directory */
			ShareDirectory directory = getCurrentShareDirectory();
			Device sender = deviceContext.getMessageDevice(message);
			try {
				sender.sendMessage(Message.createShareDirectoryMessage(directory));
			} catch (Exception e) {
				Configs.log("", e);
			}
		} else if (type == Message.TYPE_SHARE_DIRECTORY) {
			ShareDirectoryMessage shareDirectoryMessage = (ShareDirectoryMessage) message;
			final ShareDirectory directory = shareDirectoryMessage.getShareDirectory();
			final Device sender = deviceContext.getMessageDevice(message);
			AlertDialog.Builder builder = new AlertDialog.Builder(context);
			builder.setTitle("Share Directory of " + sender.getName() + ", items(" + directory.getItems().size() + ")");
			ListView lv = new ListView(context);
			lv.setAdapter(new BaseAdapter() {
				
				@Override
				public View getView(int position, View convertView, ViewGroup parent) {
					TextView tv = new TextView(context);
					final ShareItem shareItem = directory.getItems().get(position);
					tv.setText(shareItem.toString());
					tv.setOnClickListener(new View.OnClickListener() {
						
						@Override
						public void onClick(View v) {
							try {
								sender.sendMessage(Message.createRequestFileMessage(shareItem.fileInfo));
							} catch (Exception e) {
								Log.e("", "", e);
							}
						}
					});
					return tv;
				}
				
				@Override
				public long getItemId(int position) {
					return position;
				}
				
				@Override
				public Object getItem(int position) {
					return directory.getItems().get(position);
				}
				
				@Override
				public int getCount() {
					return directory.getItems().size();
				}
			});
			builder.setView(lv);
			builder.show();
		} else if (type == Message.TYPE_REQUEST_FILE) {
			RequestFileMessage requestFileMessage = (RequestFileMessage) message;
			Device sender = deviceContext.getMessageDevice(requestFileMessage);
			try {
				sender.sendMessage(Message.createImageMessage(new File(requestFileMessage.getInfo().path), Message.GROUP_SHARE));
			} catch (Exception e) {
				Log.e("", "", e);
			}
		}
	}

	/* implement interface UserAction */

	private static long getMessageGroup(SendTarget sendTarget) {
		if (sendTarget instanceof BroadCastTarget) {
			return Message.GROUP_BROADCAST;
		}
		if (sendTarget instanceof Device) {
			return Message.GROUP_ONE2ONE;
		}
		if (sendTarget instanceof Group) {
			Group group = (Group) sendTarget;
			return group.getGroupid();
		}
		return Message.GROUP_ONE2ONE;
	}

	@Override
	public void sendText(String text, SendTarget target) {
		TextMessage message = Message.createTextMessage(text, getMessageGroup(target));
		try {
			target.sendMessage(message);
		} catch (IOException e) {
			Configs.log("", e);
		}
	}

	@Override
	public void sendFile(File file, SendTarget target) {
		FileMessage fileMessage = Message.createFileMessage(file, getMessageGroup(target));
		try {
			target.sendMessage(fileMessage);
		} catch (IOException e) {
			Configs.log("", e);
		}
	}

	@Override
	public void rejectFile(FileMessage fileMessage) {
		final Device sender = deviceContext.getMessageDevice(fileMessage);
		try {
			sender.sendMessage(Message.createRejectFileMessage(fileMessage));
		} catch (Exception e) {
			Configs.log("", e);
		}
	}

	@Override
	public void sendImage(File imagefile, SendTarget target) {
		ImageMessage imageMessage = Message.createImageMessage(imagefile, getMessageGroup(target));
		try {
			target.sendMessage(imageMessage);
		} catch (IOException e) {
			Configs.log("", e);
		}
	}

	@Override
	public Group createGroup(GroupParam param) {
		Group group = new Group();
		group.setGroupid(Configs.generateUniqueGroupId());
		group.setParam(param);
		deviceContext.addGroup(context, group, deviceContext.getLocal());
		myBroadcastMessage.addGroup(group);
		broadCastSender.broadcastNow();
		return group;
	}

	@Override
	public void joinGroup(Group group) {
		myBroadcastMessage.addGroup(group);
		deviceContext.addGroup(context, group, deviceContext.getLocal());
		broadCastSender.broadcastNow();
	}

	@Override
	public void leaveGroup(Group group) {
		myBroadcastMessage.removeGroup(group);
		long deviceId = deviceContext.getLocal().getId();
		long groupId = group.getGroupid();
		deviceContext.leaveGroup(context, groupId, deviceId);
		try {
			BroadCastTarget.getInstance().sendMessage(Message.createLeaveGroupMessage(groupId));
		} catch (Exception e) {
			Configs.log("send leave group failed", e);
		}
	}

	@Override
	public ShareDirectory getCurrentShareDirectory() {
		String directoryXML = UserSettings.getSettingString(context, UserSettings.Keys.SHARE_DIRECTORY);
		if (directoryXML == null || "".equals(directoryXML))
			return new ShareDirectory();
		Persister p = new Persister();
		try {
			ShareDirectory shareDirectory = p.read(ShareDirectory.class, directoryXML);
			return shareDirectory;
		} catch (Exception e) {
			Log.e("", "", e);
			return new ShareDirectory();
		}
	}
	
	private void saveShareDirectory(ShareDirectory directory) {
		Persister p = new Persister();
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		try {
			p.write(directory, os);
		} catch (Exception e) {
			Log.e("", "", e);
		}
		UserSettings.putSettingString(context, UserSettings.Keys.SHARE_DIRECTORY, os.toString());
	}

	@Override
	public void addShare(ShareItem item) {
		ShareDirectory directory = getCurrentShareDirectory();
		directory.addItem(item);
		saveShareDirectory(directory);
	}

	@Override
	public void removeShare(ShareItem item) {
		ShareDirectory directory = getCurrentShareDirectory();
		directory.removeItem(item);
		saveShareDirectory(directory);
	}

	public DeviceContext getDeviceContext() {
		return deviceContext;
	}

	public Device getLocalDevice() {
		return deviceContext.getLocal();
	}

}
